package test.threads.queue.stressed.cpu;

import java.util.Arrays;

import net.jcip.annotations.NotThreadSafe;
import test.helpers.Config;
import test.threads.queue.common.IUsefulWorkEmulator;
import test.threads.queue.common.IUsefulWorkFactory;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * @author cheremin
 * @since 02.03.12,  15:41
 */
@NotThreadSafe
public class CPUWasterFactory implements IUsefulWorkFactory<LongValueMessage> {

    private final int opsPerPacket;

    public CPUWasterFactory( final Config config ) {
        opsPerPacket = config.getAsInt( "payload.ops-per-packet" );
    }

    private int operationsPerNode;

    @Override
    public IUsefulWorkEmulator<LongValueMessage>[] createEmulators( final int count ) {
        checkArgument( opsPerPacket % count == 0, "opsPerPacket(%s) % workerCount(%s) must be 0", opsPerPacket, count );
        operationsPerNode = opsPerPacket / count;

        final IUsefulWorkEmulator<LongValueMessage>[] emulators = new IUsefulWorkEmulator[count];
        final CPUTimeWaster cpuTimeWaster = new CPUTimeWaster( operationsPerNode );
        Arrays.fill( emulators, cpuTimeWaster );
        return emulators;
    }

    private long index = 0;

    @Override
    public LongValueMessage newInstance() {
        final LongValueMessage message = LongValueMessage.FACTORY.newInstance();
        message.value = index++;
        return message;
    }

    @Override
    public String toString() {
        return "cpu[" + operationsPerNode + " ops/node]";
    }

    /**
     * Just simple mul/add -- only consume CPU cycles
     *
     * @author cheremin
     * @since 27.02.12,  13:42
     */
    private static class CPUTimeWaster implements IUsefulWorkEmulator<LongValueMessage> {
        private static final long MUL_CONSTANT = 42 * 37;
        private final int time;

        public CPUTimeWaster( final int time ) {
            checkArgument( time > 0, "time(%s) must be > 0", time );
            this.time = time;
        }

        @Override
        public long spendCPUTime( final LongValueMessage message ) {
            long value = message.value;
            for ( int i = 0; i < time; i++ ) {
                value = next( value );
            }
            message.value = value;
            return value;
        }

        private long next( final long current ) {
            return current * MUL_CONSTANT + 42;
        }
    }
}
